package com.netty.handler;

import com.alibaba.fastjson.JSON;
import com.netty.domain.IMessage;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author renyitong
 * @version 1.0.0
 * @createTime 2023年08月16日
 * @Description 服务端自定义处理入站消息
 */
@Component
@ChannelHandler.Sharable
@Slf4j
public class WebSocketNettyHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 存储用户对应的通道
     */
    private static Map<String, ChannelHandlerContext> MAP = new ConcurrentHashMap<>(16);

    /**
     * 存放通道到用户关联
     */
    private static Map<String, String> CHANNEL_USER = new ConcurrentHashMap<>(16);

    /**
     * 存储当前连接上的通道
     */
    private static List<ChannelHandlerContext> LIST = new CopyOnWriteArrayList<>();


    /**
     * 通道连接事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LIST.add(ctx);
        log.info("有新的连接. >>当前连接数量: {}", LIST.size());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        log.info("前端发来消息: {}", msg.text());
        IMessage message = JSON.parseObject(msg.text(), IMessage.class);
        if (message.getType().equals(1)) {
            setMap(ctx, message);
            // 给其他服务器发送上线消息
            for (ChannelHandlerContext handlerContext : MAP.values()) {
                if (handlerContext == ctx) {
                    continue;
                }
                handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
            }
            return;
        }

        // 获取到需要转发的客户端
        String receive = message.getReceive();
        // 没有指定接收者代表要群发
        if (StringUtil.isNullOrEmpty(receive)) {
            for (ChannelHandlerContext handlerContext : MAP.values()) {
                if (handlerContext == ctx) {
                    continue;
                }
                handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
            }
            return;
        }

        // 从缓存的存储用户对应的通道 map中获取
        if (!MAP.containsKey(receive)) {
            IMessage message1 = new IMessage("服务端", ctx.name(), UUID.randomUUID().toString(), "用户" + message.getReceive() + "未在线，你的消息不能及时送达。", 2);
            ctx.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message1)));
            return;
        }

        // 服务端转发消息到指定的客户端
        ChannelHandlerContext channelHandlerContext = MAP.get(receive);
        channelHandlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
    }

    /**
     * 设置连接映射
     *
     * @param context
     * @param message
     */
    private void setMap(ChannelHandlerContext context, IMessage message) {
        MAP.put(message.getSend(), context);
        CHANNEL_USER.put(context.channel().id().toString(), message.getSend());
    }

    /**
     * 通达关闭事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String send = CHANNEL_USER.get(ctx.channel().id().toString());

        // 给其他在线的用户去发送消息
        for (ChannelHandlerContext handlerContext : MAP.values()) {
            IMessage message = new IMessage("服务端", null, UUID.randomUUID().toString(), "用户" + send + "--已经离线了", 2);
            handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
        }
        LIST.remove(ctx);
        CHANNEL_USER.remove(ctx.channel().id().toString());
    }


    /**
     * 出现了链接异常
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        String send = CHANNEL_USER.get(ctx.channel().id().toString());
        MAP.remove(send);
        // 给其他在线用户发送该用户离线的信息
        for (ChannelHandlerContext handlerContext : MAP.values()) {
            IMessage message = new IMessage("服务端", null, UUID.randomUUID().toString(), "用户--" + send + "--连接发生问题，已被迫离线了", 2);
            handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
        }
        LIST.remove(ctx);
        CHANNEL_USER.remove(ctx.channel().id().toString());
    }

    public static Map<String, ChannelHandlerContext> getMAP() {
        return MAP;
    }

    public static void setMAP(Map<String, ChannelHandlerContext> MAP) {
        WebSocketNettyHandler.MAP = MAP;
    }

    public static Map<String, String> getChannelUser() {
        return CHANNEL_USER;
    }

    public static void setChannelUser(Map<String, String> channelUser) {
        CHANNEL_USER = channelUser;
    }

    public static List<ChannelHandlerContext> getLIST() {
        return LIST;
    }

    public static void setLIST(List<ChannelHandlerContext> LIST) {
        WebSocketNettyHandler.LIST = LIST;
    }
}
